/*
 * $Id: Socket.h,v 1.19 2006-02-22 09:13:57 bacon Exp $
 */

#ifndef _XPP_NET_SOCKET_CLASS_
#define _XPP_NET_SOCKET_CLASS_

#include <xp/types.h>
#include <xp/macros.h>
#include <xp/net/sck.h>
#include <xpp/Uncopyable.h>
#include <xpp/net/Address.h>
#include <xpp/net/Transmittable.h>

#define XPP_NET_SOCKET_CHECK_OPEN_HANDLE(x) \
	do { \
		if ((x) != INVALID_HANDLE) { \
			this->error_code = ERR_OPEN; return -1; \
		} \
	} while (0)

#define XPP_NET_SOCKET_CHECK_CLOSED_HANDLE(x) \
	do { \
		if ((x) == INVALID_HANDLE) { \
			this->error_code = ERR_CLOSED; return -1; \
		} \
	} while (0)

/*
#ifdef _WIN32
	#define XPP_NET_SOCKET_ERRNO (WSAGetLastError())
#else
*/
	#define XPP_NET_SOCKET_ERRNO (xp_errno)
//#endif

namespace xpp
{
	namespace net
	{

		class Socket: protected xp_sck_t, 
			public Uncopyable, public Transmittable 
		{
		public:
			friend class TcpSocket;

			typedef xp_sckhnd_t Handle;

			enum 
			{
				INVALID_HANDLE = XP_SCKHND_INVALID
			};

			enum 
			{
				RAW      = SOCK_RAW,
				STREAM   = SOCK_STREAM,
				DATAGRAM = SOCK_DGRAM
			};

			enum 
			{
				ERR_TYPE_MISMATCH = Transmittable::__NERRCODES__,
				ERR_TYPE_RETRIEVAL,
				ERR_INVALID,
				ERR_OPEN,
				ERR_CLOSED,
				ERR_NOSUPPORT,
				ERR_ADDRINUSE,
				ERR_ACCESS,
				ERR_REFUSED,
				ERR_UNREACHABLE,
				ERR_NOTCONNECTED,
				ERR_NORESOURCE,
				ERR_CREATE,
				ERR_SOCKOPT,
				__NERRCODES__
			};

			typedef Address::addr_t addr_t;
			typedef Address::len_t  len_t;

			Socket (int type);
			virtual ~Socket ();

			static int startup (bool try_v6 = true);
			static int cleanup  ();

			bool isOpen ()
			{
				return this->sckhnd != INVALID_HANDLE;
			}

			virtual int open (Handle handle);
			virtual int open (Handle handle, const Address& target) 
			{ 
				this->error_code = ERR_NOSUPPORT;
				return -1; 
			}
			virtual int open (const Address& target)
			{ 
				this->error_code = ERR_NOSUPPORT;
				return -1; 
			}

			virtual int close ();
			virtual int shutdown (int how = 2);

			/*virtual*/ int errorCode () const
			{
				return error_code;
			}

			/*virtual*/ bool isValid () const
			{
				return this->sckhnd != INVALID_HANDLE;
			}

			/*virtual*/ Handle handle () const
			{
				return this->sckhnd;
			}

			/*virtual*/ xp_time_t timeout () const 
			{
				//return this->socket_timeout; 
				return xp_sck_gettmout (
					(xp_sck_t*)this, XP_SCK_ALL);
			}
			/*virtual*/ void enableTimeout (xp_time_t t)
			{ 
				//this->socket_timeout = t;
				xp_sck_settmout (this, XP_SCK_ALL, t);
			}
			/*virtual*/ void disableTimeout ()
			{
				//this->socket_timeout = -1;
				xp_sck_settmout (this, XP_SCK_ALL, -1);
			}
			/*virtual*/ bool isTimeoutEnabled () const 
			{ 
				//return this->socket_timeout >= 0;
				return xp_sck_gettmout(
					(xp_sck_t*)this, XP_SCK_ALL) >= 0;
			}
		
			virtual xp_ssize_t send (
				const void* data, xp_size_t size, int flags = 0);
			virtual xp_ssize_t sendAll (
				const void* data, xp_size_t size, int flags = 0);
			virtual xp_ssize_t receive (
				void* buf, xp_size_t size, int flags = 0);
			virtual xp_ssize_t receiveAll (
				void* buf, xp_size_t size, int flags = 0);

			/*virtual*/ int getOption (
				int level, int optname, void* optval, len_t* optlen);
			/*virtual*/ int setOption (
				int level, int optname, const void* optval, len_t optlen);

			/*virtual*/ int  setDebug       (int n);
			/*virtual*/ int  setReuseAddr   (int n);
			/*virtual*/ int  setKeepAlive   (int n);
			/*virtual*/ int  setBroadcast   (int n);
			/*virtual*/ int  setSendBuf     (unsigned int size);
			/*virtual*/ int  setRecvBuf     (unsigned int size);
			/*virtual*/ int  setLingerOn    (int sec);
			/*virtual*/ int  setLingerOff   ();
			/*virtual*/ int  setTcpNodelay  (int n);
			/*virtual*/ int  setOobInline   (int n);
			/*virtual*/ int  setIpv6Only    (int n);
			/*virtual*/ int  bindToDevice   (const xp_char_t* device);
			/*virtual*/ int  unbindToDevice ();

			// tri-state 1: supported, 0: unknown, -1 not supported
			static int v6_supported;

		protected:
			int socket_type;
			//xp_time_t socket_timeout;

			int create_socket (int domain, int type, int proto);
			int bind_socket (addr_t* addr, len_t len);

			int connect_socket (addr_t* addr, len_t len);
			int connect_socket (addr_t* addr, len_t len, xp_bool_t (*cancel) (void*));

			int listen_socket (int backlog);
			int translate_errno (int num, int def_err);
		};

	}
}

#endif
